#include <vector>
#include <string>
#include <iostream>
#include <fstream>
#include <algorithm>
#include <iterator>
#include <map>
#include <set>

#include <GL/glut.h>
#include "mesh.h"
#include "simp.h"

int WINDOW_WIDTH = 500, WINDOW_HEIGHT = 500;

// mouse state
int prevX = 0, prevY = 0;
bool leftPressed = false, rightPressed = false, middlePressed = false;

// view state
float rotMat [ 16 ] = {1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1};
float focus [ 3 ] = {0,0,-1.5};

Mesh mesh ;
inline void triangle(Point p1, Point p2, Point p3)
{
								// Drawing Using Triangles
	Point p = Point::normal((p2-p1)^(p3-p1)) ;
		glNormal3f (p.x, p.y, p.z);
		glVertex3f( p1.x, p1.y, p1.z);				// Top
		glVertex3f( p2.x, p2.y, p2.z);				// Bottom Left
		glVertex3f( p3.x, p3.y, p3.z);				// Bottom Right
		
		
}
inline void drawTriangles (stdext::hash_map <EdgeHashKey, Edge * > & edges)
{
	
	stdext::hash_map <FaceHashKey, int>  faces_cache; 
	for (stdext::hash_map <EdgeHashKey, Edge * >::iterator iter = edges.begin();
		iter != edges.end(); ++iter)
	{
		unsigned id1 = iter->second->he->from_v->idx ;
		unsigned id2 = iter->second->he->next->from_v->idx ;
		unsigned id3 = iter->second->he->next->next->from_v->idx ;

		if (faces_cache.find(FaceHashKey(id1, id2, id3)) == faces_cache.end())
		{
//			std::cout<<"Draw this"<<std::endl;
			triangle(iter->second->he->from_v->point, 
			iter->second->he->next->from_v->point,
			iter->second->he->next->next->from_v->point);
			faces_cache[FaceHashKey(id1, id2, id3)] = 0; 
		}

		id1 = iter->second->he->flip->from_v->idx ;
		id2 = iter->second->he->flip->next->from_v->idx ;
		id3 = iter->second->he->flip->next->next->from_v->idx ;

		if (faces_cache.find(FaceHashKey(id1, id2, id3)) == faces_cache.end())
		{
//			std::cout<<"Draw this"<<std::endl;
			triangle(iter->second->he->flip->from_v->point, 
			iter->second->he->flip->next->from_v->point,
			iter->second->he->flip->next->next->from_v->point);
			faces_cache[FaceHashKey(id1, id2, id3)] = 0; 
		}
	}
	/*
	for (stdext::hash_map <FaceHashKey, Face * >::iterator iter = mesh.faces.begin(); 
		iter != mesh.faces.end(); ++iter )
	{
		unsigned id1 = iter->second->he->from_v->idx ;
		unsigned id2 = iter->second->he->next->from_v->idx ;
		unsigned id3 = iter->second->he->next->next->from_v->idx ;


//			std::cout<<"Draw this"<<std::endl;
			triangle(iter->second->he->from_v->point, 
			iter->second->he->next->from_v->point,
			iter->second->he->next->next->from_v->point);

	}
	*/
}

void display() 
{
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        // Set up viewing matrices
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        gluPerspective(60, float(WINDOW_WIDTH)/WINDOW_HEIGHT, .0001, 100);
        glMatrixMode(GL_MODELVIEW);
		glLoadIdentity();

        //Camera
        glTranslatef(focus[0], focus[1], focus[2]);
        glMultMatrixf(rotMat);


        // drawing code goes here
        //glutSolidCube ( 1 );
		glBegin(GL_TRIANGLES);
		//std::cout<<"DRAW1"<<std::endl;
		drawTriangles(mesh.edges);
		//std::cout<<"DRAW2"<<std::endl;
		glEnd();
        // end drawing code


        glFlush ();
        glutSwapBuffers();
}

void reshape(int width, int height)
{
        glViewport(0, 0, width, height);
        WINDOW_WIDTH = width;
        WINDOW_HEIGHT = height;
}

void mouse(int button, int state, int x, int y)
{
        y = WINDOW_HEIGHT - y;

        // Mouse state that should always be stored on pressing
        if (state == GLUT_DOWN)
        {
                prevX = x;
                prevY = y;
        }

        if (button == GLUT_LEFT_BUTTON)
        {
                leftPressed = state == GLUT_DOWN;
        }

        if (button == GLUT_RIGHT_BUTTON)
        {
                rightPressed = state == GLUT_DOWN;
        }
        
        if (button == GLUT_MIDDLE_BUTTON)
        {
                middlePressed = state == GLUT_DOWN;
        }
}

void motion(int x, int y)
{
        y = WINDOW_HEIGHT - y;

        float dx = (x - prevX);
        float dy = (y - prevY);

        // rotate the scene
        if (leftPressed)
        {
                glMatrixMode(GL_MODELVIEW);
                glLoadIdentity();
                glRotatef(dx, 0, 1, 0);
                glRotatef(dy, -1, 0, 0);
                glMultMatrixf(rotMat);
                glGetFloatv(GL_MODELVIEW_MATRIX, rotMat);
        }
        else if (middlePressed)
        {
                focus [ 0 ] += 0.005 * dx;
                focus [ 1 ] += 0.005 * dy;
        }
        else if (rightPressed)
        {
                focus [ 2 ] += 0.01 * dy;
        }
        
        // Store previous mouse positions
        prevX = x;
        prevY = y;

        glutPostRedisplay();
}


void keyboard(unsigned char key, int x, int y)
{
        glutPostRedisplay();
}

void init(void)
{
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);

        glClearColor(0, 0, 0, 1);   
        glEnable(GL_DEPTH_TEST);
        glEnable(GL_LIGHT0);
        glEnable(GL_LIGHTING);

        glEnable(GL_CULL_FACE);
}


int main(int argc, char **argv)
{
        if (argc < 3)
        {
                std::cout<<"error"<<std::endl;
                return 1;
        }
		
        std::ifstream ifs(argv[1]);
		//std::ifstream ifs ("H:\\workspace\\simp\\Release\\data\\cube1.obj");
        //Mesh mesh;
        ifs >> mesh; 	
		std::cout<<"Faces   : "<<mesh.faces.size()<<std::endl;
		std::cout<<"Edges   : "<<mesh.edges.size()<<std::endl;
		std::cout<<"Vertices: "<<mesh.vertices.size()<<std::endl;
		std::cout<<"Halfedge: "<<mesh.hash_he.size()<<std::endl;
		unsigned num_faces = atol(argv[2]);
		int num_collapse_edges = (mesh.faces.size() - num_faces ) / 2  ;
		simplification(mesh, num_collapse_edges);

        glutInit(&argc, argv);
        glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
        glutInitWindowPosition(0, 0);
        glutInitWindowSize(WINDOW_WIDTH, WINDOW_HEIGHT);
        glutCreateWindow("CSCE 645 - Your Name Here");

        init();

        glutDisplayFunc(display);
        glutReshapeFunc(reshape);
        glutKeyboardFunc(keyboard);
        glutMouseFunc(mouse);
        glutMotionFunc(motion);

        // main loop
        glutMainLoop();
}

/*
int main(int argc, char * * argv)
{
		
        if (argc < 3)
        {
                std::cout<<"error"<<std::endl;
                return 1;
        }
		
        std::ifstream ifs(argv[1]);
		//std::ifstream ifs ("H:\\workspace\\simp\\Release\\data\\cube1.obj");
        Mesh mesh;
        ifs >> mesh; 	
		std::cout<<"Faces   : "<<mesh.faces.size()<<std::endl;
		std::cout<<"Edges   : "<<mesh.edges.size()<<std::endl;
		std::cout<<"Vertices: "<<mesh.vertices.size()<<std::endl;
		std::cout<<"Halfedge: "<<mesh.hash_he.size()<<std::endl;

		unsigned num_faces = atol(argv[2]);
		int num_collapse_edges = (mesh.faces.size() - num_faces ) / 2  ;
		simplification(mesh, num_faces);



		std::cout<<"Eulear Number "<<
			int (mesh.faces.size())  - 
			int (mesh.edges.size()) + 
			int (mesh.vertices.size())<<std::endl;

		//Test Save Edge ;
		

        return 0;
}
*/